package org.example;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @program: itcast-MyRPC
 * @description: 1.解决并发问题（BIO串行问题）
 * * 1.解决传统BIO其中一个客户端连接成功，不发送数据，其他客户端无法连接
 * @author: zhanghz001
 * @create: 2021-02-19 14:21
 **/
public class NioSocketServer {
    public static void main(String[] args) throws IOException {
        //打开socket的nio服务,开启统一的channel监控
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //绑定监控的端口,绑定地址信息(主机地址和端口号),主机多个ip,选择127.0.0.1        
        serverSocketChannel.bind(new InetSocketAddress("127.0.0.1", 65534));
        //设置不阻塞的io
        serverSocketChannel.configureBlocking(false);
        // 打开选择器
        Selector selector = Selector.open();
        //将选择器注册到channel中,得到一个 key
        //SelectionKey.OP_ACCEPT接收连接进行事件，表示服务器监听到了客户连接，
        // 那么服务器可以接收这个连接了
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        while (true) {
            System.out.println("服务器等待客户端连接,阻塞中....");
            //获取到客户端的接入数量
            int count = selector.select();
            if (count != 0) {
                //获取已选择的键的集合
                Set<SelectionKey> set = selector.selectedKeys();
                //遍历集合
                Iterator<SelectionKey> iterator = set.iterator();
                while (iterator.hasNext()) {
                    //获取每一个selectKey
                    SelectionKey selectionKey = iterator.next();
                    if (selectionKey.isAcceptable()) {
                        System.out.println("客户端连接成功,但是没有发送数据");
                        //就绪任务类型
                        //说明是初次链接
                        ServerSocketChannel ssc = (ServerSocketChannel)
                                selectionKey.channel();
                        //设置不阻塞的io
                        SocketChannel socketChannel = ssc.accept();
                        socketChannel.configureBlocking(false);
                        //通道注册到了选择器上,读就绪时间,说明通道已经有了可读的数据,执行读操作了
                        socketChannel.register(selector, SelectionKey.OP_READ);
                    }
                    //这个是进入读取了,需要读出其中的内容
                    else if (selectionKey.isReadable()) {
                        System.out.println("客户端发送数据成功");
                        //获取和selectionKey关联的通道
                        SocketChannel channel = (SocketChannel) selectionKey.channel();
                        //准备好缓冲区
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        int read = channel.read(buffer);
                        while (read > 0) {
                            buffer.flip();
                            System.out.println(new String(buffer.array(), 0, read));
                            buffer.clear();
                            read = channel.read(buffer);
                        }
                        //释放资源
                        channel.close();
                    }
                    //任务执行完成后,selectionKey从已选择的键的集合中移除掉
                    iterator.remove();
                }
                
            }
            
        }
    }
}
